/**
 * Copyright (c) 2009 Cycle Information Technology
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this 
 * software and associated documentation files (the "Software"), to deal in the Software 
 * without restriction, including without limitation the rights to use, copy, modify, 
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 
 * permit persons to whom the Software is furnished to do so, subject to the following 
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies 
 * or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 
 */
package com.cycleit.cycleframework.entity {
	import com.cycleit.cycleframework.event.NavigationFinishEvent;
	import com.cycleit.cycleframework.exception.CycleException;
	import com.cycleit.cycleframework.interfaces.INavigableView;
	import com.cycleit.cycleframework.model.NavigationModel;
	
	import flash.events.Event;
	
	import mx.binding.utils.ChangeWatcher;
	import mx.events.PropertyChangeEvent;

	/**
	 * Clase encargada de mantener la coherencia de las vistas dentro de la aplicación.
	 * Ella será la encargada de notificar regeneraciones de vista y de realizar la limpieza 
	 * de listener de los elementos no visibles.
	 */ 
	public class NavigationView{
		private var eraseable:Boolean;
		private var cleanChilds:Boolean;
		private var imAdded:Boolean = false;
		private var view:INavigableView;
		private var watcherChangeState:ChangeWatcher;
		private var _navigationModel:NavigationModel = NavigationModel.getInstance();
		private var _myIndex:uint = _navigationModel.getNewIndex();
		/**
		 * Metodo utilizado para conocer el índice de una vista dentro del 
		 * árbol de navegación 
		 * 
		 * @return uint el índice de vista
		 * 
		 */
		public function get myIndex():uint {
			return _myIndex;
		}
		/**
		 * Constructor que inicializa los datos necesarios para el correcto funcionamiento 
		 * del INavigableView asociado.
		 *  
		 * @param nv instancia INavigableView al que pertenece.
		 * @param eraseable Si queremos que la vista se destruya tras quitarla de la jerarquía
		 *      			 de componentes visuales, o si por el contrario no queremos borrarla 
		 * 				     y vamos a mantener una referencia para optimizar la aplicación 
		 * 					 instanciando menos objetos.
		 * 
		 * @see com.cycleit.cycleframework.interfaces.INavigableView
		 */		
		public function NavigationView(nv:INavigableView, eraseable:Boolean = false, cleanChilds:Boolean = true) {
			this.view = nv;
			this.eraseable = eraseable;
			this.cleanChilds = cleanChilds;
			//creamos el watcher para escuchar los cambios de vista
			watcherChangeState = ChangeWatcher.watch(_navigationModel.navigationFlags[myIndex], "changes", navigateTo);
			view.addEventListener("render",initNavigableComponent);
		}
		
		private function initNavigableComponent(e:Event):void {
			trace(e.type + " " + e.target);
			if (!imAdded){
				var viewCast:INavigableView = view;
				viewCast.removeEventListener("render",initNavigableComponent);
				if(!eraseable){
					imAdded = true;
					if(!viewCast.hasEventListener("addedToStage")){
						viewCast.addEventListener("addedToStage",initNavigableComponent);
					}else{
						_myIndex = _navigationModel.getNewIndex();
						watcherChangeState = ChangeWatcher.watch(_navigationModel.navigationFlags[myIndex], "changes", navigateTo);
					}
				}
				//propertyChangeEvent a mano para proseguir la creación de vista si es necesario
				var p:PropertyChangeEvent = new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE);
				p.newValue = true;
				//invocamos navigateTo para iniciar la creación de vista si hay más niveles de navegación
				this.navigateTo(p);
			}
		}
		
		private function cleanChildren():void {
			if (view.mainContainer==null) {
				throw new CycleException(CycleException.MAIN_CONTAINER_NEEDED);
			}
			//decimos a nuestros hijos que eliminen sus watcher
			var childrenCount:uint = view.mainContainer.numChildren;
			for (var i:uint = 0; i < childrenCount; i++) {
				var child:INavigableView = (view.mainContainer.getChildAt(i) as INavigableView);
				if (child) {
					child.navigationView.preRemoveFromStage();
				}
			}
		}
		
		private function preRemoveFromStage():void {
			//Eliminamos nuestro watcher y les decimos a nuestros hijos que hagan lo propio
			imAdded = false;
			watcherChangeState.unwatch();
			var childrenCount:uint = view.mainContainer.numChildren;
			for (var i:uint = 0; i < childrenCount; i++) {
				var child:INavigableView = (view.mainContainer.getChildAt(i) as INavigableView);
				if (child) {
					child.navigationView.preRemoveFromStage();
				}
			}
			if(cleanChilds){
				while (view.mainContainer.numChildren > 0) {
					view.mainContainer.removeChildAt(0);
				}
			}
		}
		
		private function navigateTo(e:PropertyChangeEvent):void {
			//es el nuevo valor del flag lo que miramos
			if(e.newValue){
				cleanChildren();
				if(myIndex+1 < _navigationModel.currentState.length){
					var nextState:String = _navigationModel.currentState[myIndex+1];
					view.navigateTo(nextState);
				}else{
					view.dispatchEvent(new NavigationFinishEvent());
				}
			}
		}
		
	}
}